求值顺序

  在一个表达式里,子表达式的求值顺序是没有定义的。特别是,你不能假定表达式从左到右求值。例如,

int x = f(2) + g(3);        // 没定义f()或g()哪个先调用

不对表达式的求值顺序加以限制,使得具体实现中有可能生成更好的代码。当然,对求值顺序不加限制也可能导致无定义的结果。例如,

    int i = 1;
    v[i] = i++;        // 无定义结果

这可能求值出v[1]=1或者v[2]=1,也可能导致更奇怪的行为。编译器可以对这样的歧义性提出警告。不幸的是,大部分编译器都不这样做。

  运算符,(逗号),&&(逻辑与)和||(逻辑或)保证了位于它们左边的运算对象一定在右边运算对象之前求值。例如,b=(a=2,a+1)将把3赋给b。关于使用&&和||的例子可以在6.2.3节找到。对于内部类型,只有在&&的第一个运算对象得到true的情况下才会对第二个运算对象求值;而||的第二哥运算对象只是在第一个运算对象得到false时才会被求值;这种做法有时被称为短路求值。请注意,序列运算符,(逗号)与用于分隔函数调用参数的逗号在逻辑上是完全不同的。考虑

f1(v[i], i++);        // 两个参数
f2((v[i], i++));      // 一个参数

对f1的调用有两个参数,v[i]和i++,而且参数求值的顺序没有定义。依赖于参数表达式的求值顺序是一种非常糟糕的风格,而且具有未定义的行为。对f2的调用只有一个参数,即逗号表达式(v[i],i++),它等价于i++。

  括号可以用做强制性的结组。例如,a*b/c表示(a*b)/c,要得到a*(b/c)就必须使用括号。如果用户不会发现其中的差异的话,a*(b/c)页可能被按照(a*b)/c的方式求值。特别地,对于许多浮点计算而言,a*(b/c)和(a*b)/c是截然不同的,所以编译器会按照写出的方式计算。

🔚